/*!
\copyright  Copyright (c) 2020-2023 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      Implementation of audio router functions.
*/

#include "audio_router.h"
#include "audio_router_observer.h"
#include "audio_router_connect.h"
#include "audio_router_typedef.h"
#include "audio_router_data.h"

#include "audio_sources.h"
#include "voice_sources.h"
#include "feature_manager.h"
#include "audio_info.h"
#include "context_framework.h"

#include <panic.h>
#include <logging.h>
#include <stdlib.h>


static feature_state_t audioRouter_GetVoiceFeatureState(void);

static audio_router_t* router_instance_handlers = NULL;
static feature_manager_handle_t voice_feature_manager_handle = NULL;
static feature_state_t voice_feature_state = feature_state_idle;
static const feature_interface_t voice_feature_manager_if =
{
    .GetState = audioRouter_GetVoiceFeatureState,
    .Suspend = NULL,
    .Resume = NULL
};

static feature_state_t audioRouter_GetVoiceFeatureState(void)
{
    return voice_feature_state;
}

static void audioRouter_PanicIfSourceInvalid(generic_source_t source)
{
    switch(source.type)
    {
        case source_type_voice:
            PanicFalse(source.u.voice < max_voice_sources);
            break;
        case source_type_audio:
            PanicFalse(source.u.audio < max_audio_sources);
            break;
        default:
            Panic();
            break;
    }
}

void AudioRouter_Init(void)
{
    DEBUG_LOG("AudioRouter_Init called.");

    voice_feature_manager_handle = FeatureManager_Register(feature_id_sco, &voice_feature_manager_if);

    AudioInfo_Init();
}

void AudioRouter_ConfigureHandlers(const audio_router_t* handlers)
{
    DEBUG_LOG_FN_ENTRY("AudioRouter_ConfigureHandlers");

    PanicNull((void*)handlers);

    router_instance_handlers = (audio_router_t*)handlers;
}

void AudioRouter_AddSource(generic_source_t source)
{
    DEBUG_LOG_FN_ENTRY("AudioRouter_AddSource");

    audioRouter_PanicIfSourceInvalid(source);

    PanicNull(router_instance_handlers);

    router_instance_handlers->add_source(source);
}

void AudioRouter_RemoveSource(generic_source_t source)
{
    DEBUG_LOG_FN_ENTRY("AudioRouter_RemoveSource");

    audioRouter_PanicIfSourceInvalid(source);

    PanicNull(router_instance_handlers);

    router_instance_handlers->remove_source(source);
}

bool AudioRouter_IsDeviceInUse(device_t device)
{
    DEBUG_LOG_FN_ENTRY("AudioRouter_IsDeviceInUse");

    PanicNull(router_instance_handlers);

    return router_instance_handlers->is_device_in_use(device);

}

void AudioRouter_Update(void)
{
    DEBUG_LOG_FN_ENTRY("AudioRouter_Update");

    PanicNull(router_instance_handlers);

    router_instance_handlers->update();
}

static bool audioRouter_CommonConnectAudioSource(generic_source_t source)
{
    DEBUG_LOG_FN_ENTRY("audioRouter_CommonConnectAudioSource enum:audio_source_t:%d", source.u.audio);

    bool conn_success = TRUE;

    if(!AudioRouter_ConnectAudioSource(source))
    {
        conn_success = FALSE;
        DEBUG_LOG_ERROR("ERROR - connecting audio failed");
    }

    return conn_success;
}

static bool audioRouter_CommonConnectVoiceSource(generic_source_t source)
{
    DEBUG_LOG_FN_ENTRY("audioRouter_CommonConnectVoiceSource enum:voice_source_t:%d", source.u.voice);
    bool conn_success = FALSE;

    if(FeatureManager_StartFeatureRequest(voice_feature_manager_handle))
    {
        if(AudioRouter_ConnectVoiceSource(source))
        {
            conn_success = TRUE;
        }
        else
        {
            FeatureManager_StopFeatureIndication(voice_feature_manager_handle);
        }
    }
    else
    {
        DEBUG_LOG_ERROR("ERROR - request to start voice feature denied by FeatureManager");
    }
    return conn_success;
}

bool AudioRouter_CommonConnectSource(generic_source_t source)
{
    bool conn_success = FALSE;

    DEBUG_LOG_FN_ENTRY("AudioRouter_CommonConnectSource enum:source_type_t:%d", source.type);

    audioRouter_PanicIfSourceInvalid(source);

    switch(source.type)
    {
        case source_type_voice:
            if(audioRouter_CommonConnectVoiceSource(source))
            {
                voice_feature_state = feature_state_running;
                conn_success = TRUE;
            }

            break;

        case source_type_audio:
            if (audioRouter_CommonConnectAudioSource(source))
            {
                AudioRouterData_StoreLastRoutedAudio(source.u.audio);
                conn_success = TRUE;
            }
            break;

        default:
            break;
    }
    if(conn_success)
    {
        ContextFramework_NotifyContextUpdate(context_active_source_info);
    }
    return conn_success;
}

static bool audioRouter_CommonDisconnectAudioSource(generic_source_t source)
{
    bool disconn_success = TRUE;

    if(!AudioRouter_DisconnectAudioSource(source))
    {
        DEBUG_LOG_ERROR("ERROR - disconnecting audio failed");
        disconn_success = FALSE;
    }

    return disconn_success;
}


static bool audioRouter_CommonDisconnectVoiceSource(generic_source_t source)
{
    DEBUG_LOG_FN_ENTRY("audioRouter_CommonDisconnectVoiceSource enum:voice_source_t:%d", source.u.voice);

    bool disconn_success = FALSE;

    if(AudioRouter_DisconnectVoiceSource(source))
    {
        voice_feature_state = feature_state_idle;
        FeatureManager_StopFeatureIndication(voice_feature_manager_handle);

        disconn_success = TRUE;
    }
    else
    {
        DEBUG_LOG_ERROR("ERROR - disconnecting voice failed");
    }

    return disconn_success;

}

bool AudioRouter_CommonDisconnectSource(generic_source_t source)
{
    bool disconn_success = FALSE;

    DEBUG_LOG_FN_ENTRY("AudioRouter_CommonDisconnectSource enum:source_type_t:%d", source.type);

    audioRouter_PanicIfSourceInvalid(source);

    switch(source.type)
    {
        case source_type_voice:
            disconn_success = audioRouter_CommonDisconnectVoiceSource(source);
            break;

        case source_type_audio:
            disconn_success = audioRouter_CommonDisconnectAudioSource(source);
            break;

        default:
            break;
    }
    if(disconn_success)
    {
        ContextFramework_NotifyContextUpdate(context_active_source_info);
    }
    return disconn_success;
}

void AudioRouter_CommonUpdateSourceRoutingStatus(generic_source_t source, source_routing_change_t routing_status)
{
    DEBUG_LOG_FN_ENTRY("AudioRouter_CommonUpdateSourceRoutingStatus enum:source_type_t:%d", source.type);

    audioRouter_PanicIfSourceInvalid(source);

    switch(source.type)
    {
        case source_type_voice:
            VoiceSources_OnVoiceRoutingChange(source.u.voice, routing_status);
            break;

        case source_type_audio:
            AudioSources_OnAudioRoutingChange(source.u.audio, routing_status);
            break;

        default:
            break;
    }
}

static void audioRouter_CommonOnSourceStateChange(generic_source_t source, source_state_t state,source_status_t status)
{
    DEBUG_LOG_INFO("audioRouter_CommonOnSourceStateChange enum:source_status_t:%d", status);

    audioRouter_PanicIfSourceInvalid(source);

    switch(source.type)
    {
        case source_type_audio:
            DEBUG_LOG_INFO("audioRouter_CommonOnSourceStateChange enum:audio_source_t:%d, state enum:source_state_t:%d", source.u.audio, state);
            AudioRouterObserver_OnAudioSourceStateChange(source, state);
            break;

        case source_type_voice:
            DEBUG_LOG_INFO("audioRouter_CommonOnSourceStateChange enum:voice_source_t:%d, state enum:source_state_t:%d", source.u.voice, state);
            AudioRouterObserver_OnVoiceSourceStateChange(source, state);
            break;

        default:
            Panic();
            break;
    }
}

source_status_t AudioRouter_CommonSetSourceState(generic_source_t source, source_state_t state)
{
    source_status_t response = source_status_error;

    audioRouter_PanicIfSourceInvalid(source);

    switch(source.type)
    {
        case source_type_voice:
            DEBUG_LOG_INFO("AudioRouter_CommonSetSourceState enum:voice_source_t:%d, state enum:source_state_t:%d", source.u.voice, state);
            response = VoiceSources_SetState(source.u.voice, state);
            break;

        case source_type_audio:
            DEBUG_LOG_INFO("AudioRouter_CommonSetSourceState enum:audio_source_t:%d, state enum:source_state_t:%d", source.u.audio, state);
            response = AudioSources_SetState(source.u.audio, state);
            break;

        default:
            Panic();
            break;
    }

    audioRouter_CommonOnSourceStateChange(source, state, response);

    return response;
}

bool AudioRouter_SourceIsInterrupted(generic_source_t source)
{
    bool result;

    PanicNull(router_instance_handlers);

    result = router_instance_handlers->source_is_interrupted(source);
    DEBUG_LOG_INFO("AudioRouter_SourceIsInterrupted: %u", result);
    return result;
}
